package com.example.sbbsec.domain.pay.impl;


import com.alibaba.fastjson.JSON;
import com.example.sbbsec.ConvertUtil;
import com.example.sbbsec.WxPayConfig;
import com.example.sbbsec.domain.pay.WxPayService;
import com.example.sbbsec.domain.pay.entity.ExamPayValueObject;
import com.example.sbbsec.domain.pay.entity.OpenIdResponse;
import com.example.sbbsec.domain.pay.entity.WxPayAppRequest;
import com.example.sbbsec.domain.pay.entity.WxPayOrder;
import com.example.sbbsec.domain.pay.entity.WxPayRequest;
import com.example.sbbsec.domain.pay.entity.WxPayResponse;
import com.example.sbbsec.domain.pay.entity.WxPayValue;
import com.example.sbbsec.domain.pay.repository.WxPayOrderRepository;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.persistence.EntityManagerFactory;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.UUID;


import static com.example.sbbsec.ConvertUtil.objToXml;
import static com.example.sbbsec.ConvertUtil.xmlToObj;
import static com.example.sbbsec.WxSignUtil.createSign;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * 微信支付ServiceImpl
 * Created by Arat.Wu on 2018/10/11.
 */
@Service
@Transactional
public class WxPayServiceImpl implements WxPayService {

    private final Object object= new Object[0];

    private static final Logger LOGGER = getLogger(WxPayServiceImpl.class);

    @Autowired
    private WxPayConfig wxPayConfig;

    @Autowired
    private RestTemplate restTemplate;




    /**
     * 微信统一支付
     *
     * @return wxPayAppRequest
     */
    @Override
    @Transactional
    public WxPayAppRequest wxPay(WxPayValue wxPayValue, ExamPayValueObject examPayValueObject) {
        LOGGER.info("统一支付开始");
        //构建支付信息
        final WxPayRequest wxPayRequest = this.buildWxPayRequest(buildBaseWxPayRequest(wxPayValue), examPayValueObject);

        //统一下单,微信预支付请求获取WxPayResponse数据
        LOGGER.info("统一支付,实体构建成功,orderNO:{}", wxPayRequest.getOutTradeNo());
        //支付的参数
        final String requestXML = objToXml(wxPayRequest);

        LOGGER.info("统一支付,requestXml:{}", requestXML);

        //生成本地支付订单
        final WxPayOrder wxPayOrder = new WxPayOrder();
        wxPayOrder.setExamId(examPayValueObject.getExamId());
        wxPayOrder.setOrderNo(wxPayRequest.getOutTradeNo());
        wxPayOrder.setPayOrder(examPayValueObject.getPayOrderId());
        wxPayOrder.setOrderAmount(wxPayRequest.getTotalFee());
        wxPayOrder.setCheckingType(wxPayValue.getPayType().getPayType());
        synchronized (object) {
            //判断是否存在已经支付成功的订单
            //final Integer count = wxPayOrderRepository.countByExamIdAndCheckingType(wxPayOrder.getExamId(),wxPayOrder.getCheckingType());
            //存在支付成功的订单或者生成新订单失败
//            if (count>0 || wxPayOrderRepository.insert(STMT_INSERT_WX_PAY_ORDER, wxPayOrder) < 1) {
//                LOGGER.error("统一支付,校验是否存在已支付订单,已支付订单数量:{}", count);
//                throw new RuntimeException("统一支付本地订单生成失败,存在支付成功的订单或者生成新订单失败");
//            }
            LOGGER.info("统一支付,订单实例化成功,orderNO:{}", wxPayRequest.getOutTradeNo());
        }

        //获取返回报文WxPayResponse
        LOGGER.info("统一支付,调用微信接口开始,orderNO:{}", wxPayRequest.getOutTradeNo());
        final String result = restTemplate.postForObject(wxPayConfig.getWxPayUrl(), requestXML, String.class);
        LOGGER.info("统一支付,调用微信接口结束,支付结果编码转换为UTF-8,orderNO:{}", wxPayRequest.getOutTradeNo());

        //支付结果编码转换
        final String utfResult = new String(Strings.nullToEmpty(result).getBytes(Charsets.ISO_8859_1), Charsets.UTF_8);
        LOGGER.info("统一支付,支付结果编码转换为UTF-8成功,xml转实体开始,orderNO:{}", wxPayRequest.getOutTradeNo());

        //xml转实体
        final WxPayResponse wxPayResponse = xmlToObj(utfResult, WxPayResponse.class);
        LOGGER.info("统一支付,支付结果编码转换为UTF-8成功,xml转实体结束,orderNO:{}", wxPayRequest.getOutTradeNo());

        if (!"SUCCESS".equals(wxPayResponse.getReturnCode())) {
            LOGGER.error("统一支付,微信支付初始化异常,订单号:{},异常信息{}", wxPayRequest.getOutTradeNo(), wxPayResponse.getReturnMsg());
            throw new RuntimeException(wxPayResponse.getReturnMsg());
        }
        if (!"SUCCESS".equals(wxPayResponse.getResultCode())) {
            LOGGER.error("统一支付,微信支付初始化异常,订单号:{},异常信息{}", wxPayRequest.getOutTradeNo(), wxPayResponse.getReturnMsg());
            throw new RuntimeException(wxPayResponse.getErrCode());
        }
        LOGGER.info("统一支付结束");
        return buildWxPayAppRequest(wxPayResponse, wxPayOrder.getOrderNo());
    }


    /**
     * 开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
     *
     * @param code
     * @return
     */
    private OpenIdResponse getWxOpenIdResponse(String code) {
        LOGGER.info("获取用户openid开始");
        //组装请求地址
        final String reqUrl = MessageFormat.format(wxPayConfig.getWxPayOpenIdUrl(), code);
        //调用微信接口
        final String result = restTemplate.getForObject(reqUrl, String.class);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("openId查询返回报文:{}", result);
        }
        //返回结果转实体
        final OpenIdResponse openIdResponse = JSON.parseObject(result, OpenIdResponse.class);

        if (openIdResponse == null) {
            LOGGER.error("微信支付获openId失败,返回结果为null");
            throw new RuntimeException("微信支付获openId失败");
        }
        if (!isNullOrEmpty(openIdResponse.getErrCode())) {
            LOGGER.error("微信支付获openId失败,错误码:{},错误信息:{}", openIdResponse.getErrCode(), openIdResponse.getErrMsg());
            throw new RuntimeException(openIdResponse.getErrCode() + openIdResponse.getErrMsg());
        }
        LOGGER.info("获取用户openid结束");
        return openIdResponse;
    }

    /**
     * 微信支付
     *
     * @return WxPayRequest
     */
    private WxPayRequest buildWxPayRequest(WxPayRequest wxPayRequest, ExamPayValueObject examPayValueObject) {
        LOGGER.info("build WxPayRequest start ");

        //商品描述
        wxPayRequest.setBody("pay");
        //标价金额,单位为分
        wxPayRequest.setTotalFee(new BigDecimal(Double.toString(examPayValueObject.getWrittenCharge())).multiply(new BigDecimal(100)).intValue());
        //商户订单号,商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
        wxPayRequest.setOutTradeNo("syzkwx"+examPayValueObject.getPayOrderId()+ System.currentTimeMillis());
        //将缴费表PayOrder作为附加数据
        wxPayRequest.setAttach(examPayValueObject.getPayOrderId());

        //计算签名(一次签名)
        wxPayRequest.setSign(createSign(ConvertUtil.objToMap(wxPayRequest), wxPayConfig.getWxPayKey()));

        LOGGER.info("build WxPayRequest end");

        return wxPayRequest;
    }

    /**
     * 构建微信小程序服务端支付基础请求数据
     *
     * @return wxPayRequest
     */
    private WxPayRequest buildBaseWxPayRequest(WxPayValue wxPayValue) {
        LOGGER.info("build BaseWxPayRequest start ");

        final WxPayRequest wxPayRequest = new WxPayRequest();

        //用户标识
        wxPayRequest.setOpenId(this.getWxOpenIdResponse(wxPayValue.getCode()).getOpenId());
        //APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP
        wxPayRequest.setSpBillCreateIp(wxPayValue.getIpAddress());

        //MD5
        wxPayRequest.setSignType(wxPayConfig.getWxSignType());
        //小程序id
        wxPayRequest.setAppId(wxPayConfig.getWxPayAppId());
        //商户id
        wxPayRequest.setMchId(wxPayConfig.getWxPayMchId());
        //门户信息
        wxPayRequest.setDeviceInfo(wxPayConfig.getWxPayDeviceInfo());
        //接收回调地址
        wxPayRequest.setNotifyUrl(wxPayConfig.getWxPayNotifyUrl());
        //交易类型
        wxPayRequest.setTradeType(wxPayConfig.getWxPayTradeType());
        //32随机字符串  使用uid
        wxPayRequest.setNonceStr(getNonceStr());

        LOGGER.info("build BaseWxPayRequest end");

        return wxPayRequest;
    }

    /**
     * 小程序客户端页面支付实体构建 用于小程序调起支付API
     *
     * @param payResponse 微信预支付回调
     * @return WxPayRequest
     */

    private WxPayAppRequest buildWxPayAppRequest(WxPayResponse payResponse, String orderNo) {
        LOGGER.info("build WxPayAppRequest start ");
        final WxPayAppRequest wxPayAppRequest = new WxPayAppRequest();
        wxPayAppRequest.setAppId(payResponse.getAppId());
        wxPayAppRequest.setNonceStr(payResponse.getNonceStr());
        wxPayAppRequest.setPackageData("prepay_id=" + payResponse.getPrepayId());
        //时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
        wxPayAppRequest.setTimeStamp(System.currentTimeMillis());
        wxPayAppRequest.setSignType(wxPayConfig.getWxSignType());
        //组合数据二次签名
        wxPayAppRequest.setPaySign(createSign(ConvertUtil.objToMap(wxPayAppRequest), wxPayConfig.getWxPayKey()));
        wxPayAppRequest.setOrderNo(orderNo);
        LOGGER.info("build WxPayAppRequest end");
        return wxPayAppRequest;
    }

    /**
     * 32随机字符串  使用uid
     *
     * @return 随机串
     */
    private static String getNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}
